/*******************************************************************************
 * Copyright (c) 2002 Boris Pruessmann and others. All rights reserved. This
 * program and the accompanying materials are made available under the terms of
 * the Common Public License v0.5 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/cpl-v05.html
 *
 * Contributors: Boris Pruessmann - Initial implementation.
 ******************************************************************************/
package net.sourceforge.perforce.ui.action;

import com.google.common.collect.Lists;

import net.sourceforge.perforce.core.PerforceCorePlugin;
import net.sourceforge.perforce.core.PerforceException;
import net.sourceforge.perforce.core.api.ICompositeSubmitResult;
import net.sourceforge.perforce.core.api.IPerforceServer;
import net.sourceforge.perforce.core.api.ISubmitResult;
import net.sourceforge.perforce.core.api.IWhereResult;
import net.sourceforge.perforce.core.internal.P4TeamProvider;
import net.sourceforge.perforce.core.internal.api.ChangeSpec;
import net.sourceforge.perforce.core.resources.IChangelist;
import net.sourceforge.perforce.ui.IPreSubmitOperation;
import net.sourceforge.perforce.ui.PerforceUIPlugin;
import net.sourceforge.perforce.ui.dialogs.ChangeSpecDialog;
import net.sourceforge.perforce.ui.internal.Policy;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.action.IAction;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.ui.actions.WorkspaceModifyOperation;

import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @version $Revision: 494 $
 * @author <a href="mailto:bpruessmann@users.sourceforge.net">Boris
 *         Pruessmann</a>
 */
public class SubmitAction extends PerforceAction {
  //----------------------------------------------------------------------------
  // -- Public Methods

  @Override
  public void run(IAction action) {
    final IChangelist[] changelists = getSelectedChangelists();
    Policy.assertTrue(changelists.length == 1);

    if (!preSubmit(changelists[0])) return;

    ChangeSpecDialog dialog = null;
    try {
      String buttonText = Policy.bind("ChangeSpecDialog.okButtonTextSubmit"); //$NON-NLS-1$
      dialog = ChangeSpecDialog.newInstance(getShell(), changelists[0], buttonText);
    } catch (PerforceException e) {
      handle(e);
      return;
    }

    if (dialog.open() == ChangeSpecDialog.OK) {
      final ChangeSpec changeSpec = dialog.getChangeSpec();
      changelists[0].setDescription(dialog.getDescription());
      run(new WorkspaceModifyOperation() {
        @Override
        protected void execute(IProgressMonitor monitor) throws CoreException,
            InvocationTargetException {
          monitor.beginTask("", 1000);
          monitor.setTaskName("Submitting changelist...");

          try {
            ICompositeSubmitResult compositeResult;
            try {
              IProgressMonitor subMonitor = Policy.subMonitorFor(monitor, 600);
              compositeResult = changelists[0].getServer().submit(changeSpec, subMonitor);
            } catch (PerforceException e) {
              throw new InvocationTargetException(e);
            }
            changelists[0].setDescription("");
            // Refresh Workspace
            ISubmitResult[] result = compositeResult.getResult();
            IProgressMonitor subMonitor = Policy.subMonitorFor(monitor, 100);
            subMonitor.beginTask("", result.length * 100); //$NON-NLS-1$

            List<IFile> fileList = Lists.newArrayList();
            for (int i = 0, length = result.length; i < length; i++) {
              IWhereResult whereResult =
                  changelists[0].getServer().where(result[i].getDepotFilename());

              // Refresh local file
              final IFile[] workspaceFiles =
                  PerforceCorePlugin.getWorkspace().getRoot().findFilesForLocation(
                      new Path(whereResult.getLocalFilename()));

              for (int j = 0; j < workspaceFiles.length; j++) {
                fileList.add(workspaceFiles[j]);
                if (result[i].isRefreshed()) {
                  workspaceFiles[j].refreshLocal(IResource.DEPTH_ZERO, subMonitor);
                }
              }
            }
            subMonitor.done();

            // Refresh Resource States
            IFile[] files = fileList.toArray(new IFile[fileList.size()]);
            refreshResourceStates(files, Policy.subMonitorFor(monitor, 300));

            PerforceCorePlugin.getPlugin().broadcastChangelistChanges();


            // TODO: Open own dialog
            if (!compositeResult.getStatus().isOK()) {
              throw new InvocationTargetException(
                  new PerforceException(compositeResult.getStatus()));
            }
          } catch (PerforceException e) {
            throw new InvocationTargetException(e);
          } finally {
            monitor.done();
          }
        }
      }, PROGRESS_DIALOG);
    }
  }

  // ---------------------------------------------------------------------------
  // Protected Methods

  private boolean preSubmit(IChangelist changelist) {
    IPreSubmitOperation operations[] = PerforceUIPlugin.getPlugin().getPreSubmitOperations();
    boolean[] performOperation = new boolean[operations.length];

    int ticks = 0;
    for (int i = 0; i < operations.length; i++) {
      try {
        performOperation[i] = operations[i].isEnabled(changelist);
        if (performOperation[i]) {
          if (operations[i].collectUserInput(changelist, getShell()))
            ticks++;
          else
            performOperation[i] = false;
        }
      } catch (CoreException e) {
        PerforceUIPlugin.handle(e);
        return false;
      }
    }
    ticks *= 100;
    IProgressMonitor monitor = Policy.monitorFor(null);
    monitor.beginTask("Pre Submit", ticks);
    for (int i = 0; i < operations.length; i++) {
      if (!performOperation[i]) continue;
      try {
        operations[i].perform(changelist, Policy.subMonitorFor(monitor, 100));
        if (monitor.isCanceled()) return false;
      } catch (Exception e) {
        PerforceUIPlugin.handle(e);
        return false;
      } finally {
        monitor.done();
      }
      monitor.worked(100);

    }
    return true;
  }

  @Override
  protected boolean isEnabled() {
    IChangelist[] changelists = getSelectedChangelists();
    if (changelists.length != 1) {
      return false;
    }

    IPerforceServer server = changelists[0].getServer();
    if (!(server.getClient().equals(changelists[0].getClientName()))) {
      return false;
    }

    return true;
  }

  //----------------------------------------------------------------------------
  // - Private Methods

  /**
   * Returns an array of all selected IChangelist objects.
   *
   * @return array of selected changelists.
   */
  private IChangelist[] getSelectedChangelists() {
    return SelectionHelper.getSelectedChangelists(getSelection());
  }

  /**
   * Called to update the resource sync infos of the given resources.
   *
   * @param resources the changed resources.
   * @param monitor the progress monitor.
   */
  private void refreshResourceStates(IResource[] resources, IProgressMonitor monitor) {
    Map<RepositoryProvider, List<IResource>> map =
        P4TeamProvider.getProviderMapping(resources, monitor);
    Set<RepositoryProvider> keySet = map.keySet();

    for (RepositoryProvider provider : keySet) {
      List<IResource> list = map.get(provider);
      IResource[] providerResources = list.toArray(new IResource[list.size()]);
      ((P4TeamProvider) provider)
          .refreshState(providerResources, IResource.DEPTH_INFINITE, monitor);
    }
  }
}
